/*-------------------------------------------------------------------------
 *
 * dsm_impl.c
 *	  manage dynamic shared memory segments
 *
 * 本文件提供了用于创建和销毁共享内存段的低级 API，
 * 使用几种不同的可能技术。我们将这些段称为动态的，因为它们可以在服务器
 * 生命周期中的任何点进行创建、修改和销毁。这与主共享内存段不同，后者总是
 * 恰好有一个，并且在每个 PostgreSQL 后台进程中始终映射在固定地址。
 *
 * 由于并非所有系统在这方面提供相同的原语，也并非所有原语在所有系统上都表现
 * 一致，因此我们提供了几种此功能的实现。许多系统实现了适合我们需求的 POSIX
 * 共享内存（shm_open 等），但问题是共享内存标识符存在于系统范围的扁平命名空间，
 * 这引发了与在同一系统上运行的其他进程（包括 PostgreSQL 的其他副本）发生
 * 名称冲突的令人不安的前景。一些系统仅支持较旧的 System V 共享内存接口（shmget 等），
 * 该接口也可以使用；然而，默认分配限制通常相当小，并且命名空间受限更多。
 *
 * 我们也提供了一种基于 mmap 的共享内存实现。这在通过专用文件系统提供共享内存的
 * 系统中可能很有用；通过选择此实现，用户甚至可以精确控制其共享内存段的位置。
 * 在某些系统上，shm_open 和 shmget 不可用或者由于某种原因无法使用时，它也可以作为
 * 后备。当然，将实际旋转磁盘上的文件映射并不是共享内存的好近似，因为写回可能
 * 会大幅降低性能，但几乎没有系统必须使我们依赖如此不友好的工具。
 *
 * 一如既往，Windows 需要其自己的实现。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/storage/ipc/dsm_impl.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#ifndef WIN32
#include <sys/mman.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_SYS_IPC_H
#include <sys/ipc.h>
#endif
#ifdef HAVE_SYS_SHM_H
#include <sys/shm.h>
#endif

#include "common/file_perm.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "portability/mem.h"
#include "postmaster/postmaster.h"
#include "storage/dsm_impl.h"
#include "storage/fd.h"
#include "utils/guc.h"
#include "utils/memutils.h"

#ifdef USE_DSM_POSIX
static bool fc_dsm_impl_posix(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
						   void **fc_impl_private, void **fc_mapped_address,
						   Size *fc_mapped_size, int fc_elevel);
static int	fc_dsm_impl_posix_resize(int fc_fd, off_t fc_size);
#endif
#ifdef USE_DSM_SYSV
static bool fc_dsm_impl_sysv(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
						  void **fc_impl_private, void **fc_mapped_address,
						  Size *fc_mapped_size, int fc_elevel);
#endif
#ifdef USE_DSM_WINDOWS
static bool dsm_impl_windows(dsm_op op, dsm_handle fc_handle, Size request_size,
							 void **fc_impl_private, void **mapped_address,
							 Size *mapped_size, int elevel);
#endif
#ifdef USE_DSM_MMAP
static bool fc_dsm_impl_mmap(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
						  void **fc_impl_private, void **fc_mapped_address,
						  Size *fc_mapped_size, int fc_elevel);
#endif
static int	fc_errcode_for_dynamic_shared_memory(void);

const struct config_enum_entry dynamic_shared_memory_options[] = {
#ifdef USE_DSM_POSIX
	{"posix", DSM_IMPL_POSIX, false},
#endif
#ifdef USE_DSM_SYSV
	{"sysv", DSM_IMPL_SYSV, false},
#endif
#ifdef USE_DSM_WINDOWS
	{"windows", DSM_IMPL_WINDOWS, false},
#endif
#ifdef USE_DSM_MMAP
	{"mmap", DSM_IMPL_MMAP, false},
#endif
	{NULL, 0, false}
};

/* 实现选择器。 */
int			dynamic_shared_memory_type;

/* 主区域中为 DSM 段预留的空间量。 */
int			min_dynamic_shared_memory;

/* 用于零填充的缓冲区大小。 */
#define ZBUFFER_SIZE				8192

#define SEGMENT_NAME_PREFIX			"Global/PostgreSQL"


/* ------
 * 以平台特定的方式执行低级共享内存操作，
 * 按照所选实现的要求。每个实现都
 * 必须实现以下原语。
 *
 * DSM_OP_CREATE。创建一个大小为request_size的段并
 * 进行映射。
 *
 * DSM_OP_ATTACH。映射段，大小必须为request_size。
 *
 * DSM_OP_DETACH。解除映射段。
 *
 * DSM_OP_DESTROY。解除映射段（如果已映射）。销毁
 * 段。
 *
 * 参数：
 *	 op：要执行的操作。
 *	 handle：现有对象的句柄，或对于DSM_OP_CREATE，
 *	   调用方希望创建的新句柄。
 *	 request_size：对于DSM_OP_CREATE，请求的大小。
 *	   否则，0。
 *	 impl_private：私有，实现特定的数据。对于
 *	   此后端中的共享内存段的第一次操作，将是指向NULL的指针；
 *	   之后，它将指向上一次调用时设置的值。
 *	 mapped_address：指向当前映射开始的指针；如果没有，则指向NULL。
 *	   更新为新映射地址。
 *	 mapped_size：指向当前映射大小的指针；如果没有，则指向0。
 *	   更新为新映射大小。
 *	 elevel：记录错误的级别。
 *
 * 返回值：成功返回true，失败返回false。当返回false时，
 * 应首先在指定的elevel处记录一条消息，除了在DSM_OP_CREATE遇到名称冲突的情况下，
 * 应该静默地返回false。
 * -----
 */
bool dsm_impl_op(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
			void **fc_impl_private, void **fc_mapped_address, Size *fc_mapped_size,
			int fc_elevel)
{
	Assert(fc_op == DSM_OP_CREATE || fc_request_size == 0);
	Assert((fc_op != DSM_OP_CREATE && fc_op != DSM_OP_ATTACH) ||
		   (*fc_mapped_address == NULL && *fc_mapped_size == 0));

	switch (dynamic_shared_memory_type)
	{
#ifdef USE_DSM_POSIX
		case DSM_IMPL_POSIX:
			return fc_dsm_impl_posix(fc_op, fc_handle, fc_request_size, fc_impl_private,
								  fc_mapped_address, fc_mapped_size, fc_elevel);
#endif
#ifdef USE_DSM_SYSV
		case DSM_IMPL_SYSV:
			return fc_dsm_impl_sysv(fc_op, fc_handle, fc_request_size, fc_impl_private,
								 fc_mapped_address, fc_mapped_size, fc_elevel);
#endif
#ifdef USE_DSM_WINDOWS
		case DSM_IMPL_WINDOWS:
			return dsm_impl_windows(fc_op, fc_handle, fc_request_size, fc_impl_private,
									fc_mapped_address, fc_mapped_size, fc_elevel);
#endif
#ifdef USE_DSM_MMAP
		case DSM_IMPL_MMAP:
			return fc_dsm_impl_mmap(fc_op, fc_handle, fc_request_size, fc_impl_private,
								 fc_mapped_address, fc_mapped_size, fc_elevel);
#endif
		default:
			elog(ERROR, "unexpected dynamic shared memory type: %d",
				 dynamic_shared_memory_type);
			return false;
	}
}

#ifdef USE_DSM_POSIX
/*
 * 支持POSIX共享内存的操作系统原语。
 *
 * POSIX共享内存段使用shm_open()和shm_unlink()创建和附加；
 * 尺寸或映射段等其他操作，则按共享内存段作为文件进行。
 *
 * 实际上，在某些平台上，它们可能以这种方式实现。虽然
 * POSIX共享内存段似乎旨在存在于平面命名空间中，
 * 但某些操作系统可能将其实现为文件，甚至会将
 * 对/xyz的请求视为在根目录中创建该名称文件的请求。
 * 使用这种有缺陷平台的用户应选择
 * 不同的共享内存实现。
 */
static bool fc_dsm_impl_posix(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
			   void **fc_impl_private, void **fc_mapped_address, Size *fc_mapped_size,
			   int fc_elevel)
{
	char		fc_name[64];
	int			fc_flags;
	int			fc_fd;
	char	   *fc_address;

	snprintf(fc_name, 64, "/PostgreSQL.%u", fc_handle);

	/* 处理拆卸情况。 */
	if (fc_op == DSM_OP_DETACH || fc_op == DSM_OP_DESTROY)
	{
		if (*fc_mapped_address != NULL
			&& munmap(*fc_mapped_address, *fc_mapped_size) != 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not unmap shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		*fc_mapped_address = NULL;
		*fc_mapped_size = 0;
		if (fc_op == DSM_OP_DESTROY && shm_unlink(fc_name) != 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not remove shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		return true;
	}

	/*
	 * 创建新段或打开现有段以进行附加。
	 *
	 * 尽管在返回之前将关闭FD，但似乎希望使用Reserve/ReleaseExternalFD，以减少EMFILE失败的可能性。事实上，我们不会长时间保持FD打开，这为使用ReserveExternalFD而不是AcquireExternalFD辩护。
	 */
	ReserveExternalFD();

	fc_flags = O_RDWR | (fc_op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
	if ((fc_fd = shm_open(fc_name, fc_flags, PG_FILE_MODE_OWNER)) == -1)
	{
		ReleaseExternalFD();
		if (fc_op == DSM_OP_ATTACH || errno != EEXIST)
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not open shared memory segment \"%s\": %m",
							fc_name)));
		return false;
	}

	/*
	 * 如果我们正在附加段，则确定当前大小；如果我们正在创建段，则将大小设置为请求的值。
	 */
	if (fc_op == DSM_OP_ATTACH)
	{
		struct stat fc_st;

		if (fstat(fc_fd, &fc_st) != 0)
		{
			int			fc_save_errno;

			/* 撤回已经完成的工作。 */
			fc_save_errno = errno;
			close(fc_fd);
			ReleaseExternalFD();
			errno = fc_save_errno;

			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not stat shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		fc_request_size = fc_st.st_size;
	}
	else if (fc_dsm_impl_posix_resize(fc_fd, fc_request_size) != 0)
	{
		int			fc_save_errno;

		/* 撤回已经完成的工作。 */
		fc_save_errno = errno;
		close(fc_fd);
		ReleaseExternalFD();
		shm_unlink(fc_name);
		errno = fc_save_errno;

		ereport(fc_elevel,
				(fc_errcode_for_dynamic_shared_memory(),
				 errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
						fc_name, fc_request_size)));
		return false;
	}

	/* Map it. */
	fc_address = mmap(NULL, fc_request_size, PROT_READ | PROT_WRITE,
				   MAP_SHARED | MAP_HASSEMAPHORE | MAP_NOSYNC, fc_fd, 0);
	if (fc_address == MAP_FAILED)
	{
		int			fc_save_errno;

		/* 撤回已经完成的工作。 */
		fc_save_errno = errno;
		close(fc_fd);
		ReleaseExternalFD();
		if (fc_op == DSM_OP_CREATE)
			shm_unlink(fc_name);
		errno = fc_save_errno;

		ereport(fc_elevel,
				(fc_errcode_for_dynamic_shared_memory(),
				 errmsg("could not map shared memory segment \"%s\": %m",
						fc_name)));
		return false;
	}
	*fc_mapped_address = fc_address;
	*fc_mapped_size = fc_request_size;
	close(fc_fd);
	ReleaseExternalFD();

	return true;
}

/*
 * 设置与文件描述符关联的虚拟内存区域的大小。
 * 如果需要，还要确保操作系统实际分配了虚拟内存，以避免以后出现意外情况。
 *
 * 如果被截断或分配失败，则返回非零值，并设置errno。
 */
static int fc_dsm_impl_posix_resize(int fc_fd, off_t fc_size)
{
	int			fc_rc;
	int			fc_save_errno;
	sigset_t	fc_save_sigmask;

	/*
	 * 阻止所有可阻止的信号，除了SIGQUIT。posix_fallocate()可能会运行很长时间，并且是一个全有或全无的操作。如果我们允许SIGUSR1反复中断我们（例如，由于恢复冲突），重试循环可能永远也不会成功。
	 */
	if (IsUnderPostmaster)
		sigprocmask(SIG_SETMASK, &BlockSig, &fc_save_sigmask);

	/* 将文件截断（或扩展）到请求的大小。 */
	do
	{
		fc_rc = ftruncate(fc_fd, fc_size);
	} while (fc_rc < 0 && errno == EINTR);

	/*
	 * 在Linux上，shm_open fd由tmpfs文件支持。在使用ftruncate调整大小后，文件可能会包含一个孔。访问由孔支持的内存会导致tmpfs分配页面，如果没有更多的tmpfs空间可用则会导致SIGBUS失败。因此，我们在这里要求tmpfs分配页面，以便我们现在可以优雅地失败并返回ENOSPC，而不是冒着将来出现SIGBUS的风险。
	 */
#if defined(HAVE_POSIX_FALLOCATE) && defined(__linux__)
	if (fc_rc == 0)
	{
		/*
		 * 我们仍然使用传统的EINTR重试循环来处理SIGCONT。posix_fallocate()不会自动重新启动，我们不希望在附加调试器时失败。
		 */
		pgstat_report_wait_start(WAIT_EVENT_DSM_FILL_ZERO_WRITE);
		do
		{
			fc_rc = posix_fallocate(fc_fd, 0, fc_size);
		} while (fc_rc == EINTR);
		pgstat_report_wait_end();

		/*
		 * 调用者期望设置errno，但posix_fallocate()不设置它。相反，它直接返回错误代码。因此，即使我们还会返回rc以指示成功或失败，也要设置errno。
		 */
		errno = fc_rc;
	}
#endif							/* HAVE_POSIX_FALLOCATE && __linux__ */

	if (IsUnderPostmaster)
	{
		fc_save_errno = errno;
		sigprocmask(SIG_SETMASK, &fc_save_sigmask, NULL);
		errno = fc_save_errno;
	}

	return fc_rc;
}

#endif							/* USE_DSM_POSIX */

#ifdef USE_DSM_SYSV
/*
 * 操作系统原语以支持System V共享内存。
 *
 * System V共享内存段使用shmget()、shmat()、shmdt()和shmctl()进行操作。由于System V共享内存的默认分配限制通常很低，因此POSIX功能可能更可取；但并非在所有地方都支持。
 */
static bool fc_dsm_impl_sysv(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
			  void **fc_impl_private, void **fc_mapped_address, Size *fc_mapped_size,
			  int fc_elevel)
{
	key_t		fc_key;
	int			fc_ident;
	char	   *fc_address;
	char		fc_name[64];
	int		   *fc_ident_cache;

	/*
	 * POSIX共享内存和基于mmap的共享内存使用名称来标识段。为了避免不必要的错误消息变化，我们使用句柄作为名称。
	 */
	snprintf(fc_name, 64, "%u", fc_handle);

	/*
	 * System V共享内存命名空间非常严格；名称的类型是key_t，预计是某种整数数据类型，但不一定与dsm_handle相同。由于我们使用dsm_handle跨进程标识共享内存段，这可能看起来是个问题，但实际上并不是。如果dsm_handle大于key_t，则下面的强制转换可能会从用户提供的句柄中截断一些位，但在每次使用该句柄时都会以完全相同的方式截断完全相同的位，这才是最重要的。相反，如果dsm_handle小于key_t，那么我们将不会使用可用key空间的完整范围，但这也没什么大不了的。
	 *
	 * 我们确保密钥不是负的，因为这可能不具有可移植性。
	 */
	fc_key = (key_t) fc_handle;
	if (fc_key < 1)				/* 如果类型为无符号，则避免编译器警告 */
		fc_key = -fc_key;

	/*
	 * 有一个特殊密钥IPC_PRIVATE，不能使用。如果在创建操作期间意外得到该值，只需假装它已经存在，以便调用者会重试。如果我们在其他地方遇到它，调用者传递的句柄与我们创建的任何内容不对应，这不应该发生。
	 */
	if (fc_key == IPC_PRIVATE)
	{
		if (fc_op != DSM_OP_CREATE)
			elog(DEBUG4, "System V shared memory key may not be IPC_PRIVATE");
		errno = EEXIST;
		return false;
	}

	/*
	 * 在我们可以对共享内存段执行任何操作之前，必须使用shmget将共享内存密钥映射到共享内存标识符。为了避免重复查找，我们使用impl_private存储密钥。
	 */
	if (*fc_impl_private != NULL)
	{
		fc_ident_cache = *fc_impl_private;
		fc_ident = *fc_ident_cache;
	}
	else
	{
		int			fc_flags = IPCProtection;
		size_t		fc_segsize;

		/*
		 * 在获取资源之前分配内存，以便在内存分配失败时不会泄漏资源。
		 */
		fc_ident_cache = MemoryContextAlloc(TopMemoryContext, sizeof(int));

		/*
		 * 使用shmget查找现有段时，我们必须将大小传递为0。传递大于实际大小的非零大小将导致EINVAL。
		 */
		fc_segsize = 0;

		if (fc_op == DSM_OP_CREATE)
		{
			fc_flags |= IPC_CREAT | IPC_EXCL;
			fc_segsize = fc_request_size;
		}

		if ((fc_ident = shmget(fc_key, fc_segsize, fc_flags)) == -1)
		{
			if (fc_op == DSM_OP_ATTACH || errno != EEXIST)
			{
				int			fc_save_errno = errno;

				pfree(fc_ident_cache);
				errno = fc_save_errno;
				ereport(fc_elevel,
						(fc_errcode_for_dynamic_shared_memory(),
						 errmsg("could not get shared memory segment: %m")));
			}
			return false;
		}

		*fc_ident_cache = fc_ident;
		*fc_impl_private = fc_ident_cache;
	}

	/* 处理拆卸情况。 */
	if (fc_op == DSM_OP_DETACH || fc_op == DSM_OP_DESTROY)
	{
		pfree(fc_ident_cache);
		*fc_impl_private = NULL;
		if (*fc_mapped_address != NULL && shmdt(*fc_mapped_address) != 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not unmap shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		*fc_mapped_address = NULL;
		*fc_mapped_size = 0;
		if (fc_op == DSM_OP_DESTROY && shmctl(fc_ident, IPC_RMID, NULL) < 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not remove shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		return true;
	}

	/* 如果我们正在附加它，则必须使用IPC_STAT来确定大小。 */
	if (fc_op == DSM_OP_ATTACH)
	{
		struct shmid_ds fc_shm;

		if (shmctl(fc_ident, IPC_STAT, &fc_shm) != 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not stat shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		fc_request_size = fc_shm.shm_segsz;
	}

	/* Map it. */
	fc_address = shmat(fc_ident, NULL, PG_SHMAT_FLAGS);
	if (fc_address == (void *) -1)
	{
		int			fc_save_errno;

		/* 撤回已经完成的工作。 */
		fc_save_errno = errno;
		if (fc_op == DSM_OP_CREATE)
			shmctl(fc_ident, IPC_RMID, NULL);
		errno = fc_save_errno;

		ereport(fc_elevel,
				(fc_errcode_for_dynamic_shared_memory(),
				 errmsg("could not map shared memory segment \"%s\": %m",
						fc_name)));
		return false;
	}
	*fc_mapped_address = fc_address;
	*fc_mapped_size = fc_request_size;

	return true;
}
#endif

#ifdef USE_DSM_WINDOWS

/*
 * 操作系统原语以支持Windows共享内存。
 *
 * Windows共享内存的实现是通过文件映射完成的，
 * 其可以由物理文件或系统分页文件支持。
 * 当前的实现使用系统分页文件，因为物理文件的其他效应
 * 如性能尚不明确，并且它在Windows中的主共享内存中以类似
 * 的方式使用。
 *
 * 内存映射对象是一个内核对象 - 当对它们的最后一个引用消失时，
 * 它们总是被删除，无论是通过CloseHandle显式删除，还是
 * 当包含引用的进程退出时。
 */
static bool dsm_impl_windows(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
				 void **fc_impl_private, void **fc_mapped_address,
				 Size *fc_mapped_size, int fc_elevel)
{
	char	   *fc_address;
	HANDLE		fc_hmap;
	char		fc_name[64];
	MEMORY_BASIC_INFORMATION fc_info;

	/*
	 * 将共享内存段存储在Global\命名空间中，可以允许
	 * 任何在任何会话中运行的进程访问该文件映射对象，
	 * 前提是调用者具有所需的访问权限。但为了避免
	 * 在主共享内存中遇到的问题，我们使用类似于
	 * 主共享内存的命名约定。一旦在GetSharedMemName中提到的问题
	 * 解决后，我们可以在这里进行更改。
	 */
	snprintf(fc_name, 64, "%s.%u", SEGMENT_NAME_PREFIX, fc_handle);

	/*
	 * 处理拆分情况。由于Windows在没有引用时会自动销毁对象，
	 * 我们可以将其视为与分离相同。
	 */
	if (fc_op == DSM_OP_DETACH || fc_op == DSM_OP_DESTROY)
	{
		if (*fc_mapped_address != NULL
			&& UnmapViewOfFile(*fc_mapped_address) == 0)
		{
			_dosmaperr(GetLastError());
			ereport(fc_elevel,
					(errcode_for_dynamic_shared_memory(),
					 errmsg("could not unmap shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		if (*fc_impl_private != NULL
			&& CloseHandle(*fc_impl_private) == 0)
		{
			_dosmaperr(GetLastError());
			ereport(fc_elevel,
					(errcode_for_dynamic_shared_memory(),
					 errmsg("could not remove shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}

		*fc_impl_private = NULL;
		*fc_mapped_address = NULL;
		*fc_mapped_size = 0;
		return true;
	}

	/* 创建新段或打开现有段以进行附加。 */
	if (fc_op == DSM_OP_CREATE)
	{
		DWORD		size_high;
		DWORD		size_low;
		DWORD		errcode;

		/* 偏移量>=类型的宽度是未定义的。 */
#ifdef _WIN64
		size_high = request_size >> 32;
#else
		size_high = 0;
#endif
		size_low = (DWORD) request_size;

		/* CreateFileMapping可能在成功时不清除错误代码 */
		SetLastError(0);

		fc_hmap = CreateFileMapping(INVALID_HANDLE_VALUE,	/* 使用页面文件 */
								 NULL,	/* 默认安全属性 */
								 PAGE_READWRITE,	/* 内存是读/写的 */
								 size_high, /* 大小的高32位 */
								 size_low,	/* 大小的低32位 */
								 fc_name);

		errcode = GetLastError();
		if (errcode == ERROR_ALREADY_EXISTS || errcode == ERROR_ACCESS_DENIED)
		{
			/*
			 * 在Windows上，当段已经存在时，将返回现有段的句柄。我们必须在返回之前关闭它。然而，如果现有段是由服务创建的，则会返回ERROR_ACCESS_DENIED。我们在这里不执行_dosmaperr，因此errno不会被修改。
			 */
			if (fc_hmap)
				CloseHandle(fc_hmap);
			return false;
		}

		if (!fc_hmap)
		{
			_dosmaperr(errcode);
			ereport(fc_elevel,
					(errcode_for_dynamic_shared_memory(),
					 errmsg("could not create shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
	}
	else
	{
		fc_hmap = OpenFileMapping(FILE_MAP_WRITE | FILE_MAP_READ,
							   FALSE,	/* 不要继承名称 */
							   fc_name);	/* 映射对象的名称 */
		if (!fc_hmap)
		{
			_dosmaperr(GetLastError());
			ereport(fc_elevel,
					(errcode_for_dynamic_shared_memory(),
					 errmsg("could not open shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
	}

	/* Map it. */
	fc_address = MapViewOfFile(fc_hmap, FILE_MAP_WRITE | FILE_MAP_READ,
							0, 0, 0);
	if (!fc_address)
	{
		int			save_errno;

		_dosmaperr(GetLastError());
		/* 撤回已经完成的工作。 */
		save_errno = errno;
		CloseHandle(fc_hmap);
		errno = save_errno;

		ereport(fc_elevel,
				(errcode_for_dynamic_shared_memory(),
				 errmsg("could not map shared memory segment \"%s\": %m",
						fc_name)));
		return false;
	}

	/*
	 * VirtualQuery以页面大小单位给出大小，对于Windows而言，页面大小为4K。我们只在附加时需要大小，但在创建新段时获取大小要更好，以确保在DSM_OP_CREATE和DSM_OP_ATTACH时大小一致。
	 */
	if (VirtualQuery(fc_address, &fc_info, sizeof(fc_info)) == 0)
	{
		int			save_errno;

		_dosmaperr(GetLastError());
		/* 撤回已经完成的工作。 */
		save_errno = errno;
		UnmapViewOfFile(fc_address);
		CloseHandle(fc_hmap);
		errno = save_errno;

		ereport(fc_elevel,
				(errcode_for_dynamic_shared_memory(),
				 errmsg("could not stat shared memory segment \"%s\": %m",
						fc_name)));
		return false;
	}

	*fc_mapped_address = fc_address;
	*fc_mapped_size = fc_info.RegionSize;
	*fc_impl_private = fc_hmap;

	return true;
}
#endif

#ifdef USE_DSM_MMAP
/*
 * 操作系统原语以支持基于mmap的共享内存。
 *
 * 称之为“共享内存”有些不准确，因为我们实际上是在创建一堆文件并将它们映射到我们的地址空间中。操作系统可能感到有必要将内容同步到磁盘，即使没有任何东西被分页出去，这对我们没有好处。用户可以将pg_dynshmem目录移到内存磁盘上，以避免此问题（如果可用）。
 */
static bool fc_dsm_impl_mmap(dsm_op fc_op, dsm_handle fc_handle, Size fc_request_size,
			  void **fc_impl_private, void **fc_mapped_address, Size *fc_mapped_size,
			  int fc_elevel)
{
	char		fc_name[64];
	int			fc_flags;
	int			fc_fd;
	char	   *fc_address;

	snprintf(fc_name, 64, PG_DYNSHMEM_DIR "/" PG_DYNSHMEM_MMAP_FILE_PREFIX "%u",
			 fc_handle);

	/* 处理拆卸情况。 */
	if (fc_op == DSM_OP_DETACH || fc_op == DSM_OP_DESTROY)
	{
		if (*fc_mapped_address != NULL
			&& munmap(*fc_mapped_address, *fc_mapped_size) != 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not unmap shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		*fc_mapped_address = NULL;
		*fc_mapped_size = 0;
		if (fc_op == DSM_OP_DESTROY && unlink(fc_name) != 0)
		{
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not remove shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		return true;
	}

	/* 创建新段或打开现有段以进行附加。 */
	fc_flags = O_RDWR | (fc_op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
	if ((fc_fd = OpenTransientFile(fc_name, fc_flags)) == -1)
	{
		if (fc_op == DSM_OP_ATTACH || errno != EEXIST)
			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not open shared memory segment \"%s\": %m",
							fc_name)));
		return false;
	}

	/*
	 * 如果我们正在附加段，则确定当前大小；如果我们正在创建段，则将大小设置为请求的值。
	 */
	if (fc_op == DSM_OP_ATTACH)
	{
		struct stat fc_st;

		if (fstat(fc_fd, &fc_st) != 0)
		{
			int			fc_save_errno;

			/* 撤回已经完成的工作。 */
			fc_save_errno = errno;
			CloseTransientFile(fc_fd);
			errno = fc_save_errno;

			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not stat shared memory segment \"%s\": %m",
							fc_name)));
			return false;
		}
		fc_request_size = fc_st.st_size;
	}
	else
	{
		/*
		 * 分配一个充满零的缓冲区。
		 *
		 * 注意：使用palloc zbuffer，而不是仅使用本地char数组，以确保它的对齐足够好；这可能会节省一些传输数据到内核的周期。
		 */
		char	   *fc_zbuffer = (char *) palloc0(ZBUFFER_SIZE);
		Size		fc_remaining = fc_request_size;
		bool		fc_success = true;

		/*
		 * 将文件填充为零。我们必须以粗暴的方式执行此操作，以确保所有文件空间都已真正分配，这样在访问内存映射时就不会发生段错误。这是相当低效的。
		 */
		while (fc_success && fc_remaining > 0)
		{
			Size		fc_goal = fc_remaining;

			if (fc_goal > ZBUFFER_SIZE)
				fc_goal = ZBUFFER_SIZE;
			pgstat_report_wait_start(WAIT_EVENT_DSM_FILL_ZERO_WRITE);
			if (write(fc_fd, fc_zbuffer, fc_goal) == fc_goal)
				fc_remaining -= fc_goal;
			else
				fc_success = false;
			pgstat_report_wait_end();
		}

		if (!fc_success)
		{
			int			fc_save_errno;

			/* 撤回已经完成的工作。 */
			fc_save_errno = errno;
			CloseTransientFile(fc_fd);
			unlink(fc_name);
			errno = fc_save_errno ? fc_save_errno : ENOSPC;

			ereport(fc_elevel,
					(fc_errcode_for_dynamic_shared_memory(),
					 errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
							fc_name, fc_request_size)));
			return false;
		}
	}

	/* Map it. */
	fc_address = mmap(NULL, fc_request_size, PROT_READ | PROT_WRITE,
				   MAP_SHARED | MAP_HASSEMAPHORE | MAP_NOSYNC, fc_fd, 0);
	if (fc_address == MAP_FAILED)
	{
		int			fc_save_errno;

		/* 撤回已经完成的工作。 */
		fc_save_errno = errno;
		CloseTransientFile(fc_fd);
		if (fc_op == DSM_OP_CREATE)
			unlink(fc_name);
		errno = fc_save_errno;

		ereport(fc_elevel,
				(fc_errcode_for_dynamic_shared_memory(),
				 errmsg("could not map shared memory segment \"%s\": %m",
						fc_name)));
		return false;
	}
	*fc_mapped_address = fc_address;
	*fc_mapped_size = fc_request_size;

	if (CloseTransientFile(fc_fd) != 0)
	{
		ereport(fc_elevel,
				(errcode_for_file_access(),
				 errmsg("could not close shared memory segment \"%s\": %m",
						fc_name)));
		return false;
	}

	return true;
}
#endif

/*
 * 当段即使在没有后端附加时也要被保留时，必须执行的特定于实现的操作。
 *
 * 除非在Windows上，我们实际上不需要做任何事情。但由于Windows在没有引用时会自动清理段，因此我们将段句柄复制到postmaster进程中。postmaster无需做任何事情即可接收句柄；Windows会自动传输它。
 */
void dsm_impl_pin_segment(dsm_handle fc_handle, void *fc_impl_private,
					 void **fc_impl_private_pm_handle)
{
	switch (dynamic_shared_memory_type)
	{
#ifdef USE_DSM_WINDOWS
		case DSM_IMPL_WINDOWS:
			if (IsUnderPostmaster)
			{
				HANDLE		hmap;

				if (!DuplicateHandle(GetCurrentProcess(), fc_impl_private,
									 PostmasterHandle, &hmap, 0, FALSE,
									 DUPLICATE_SAME_ACCESS))
				{
					char		fc_name[64];

					snprintf(fc_name, 64, "%s.%u", SEGMENT_NAME_PREFIX, fc_handle);
					_dosmaperr(GetLastError());
					ereport(ERROR,
							(errcode_for_dynamic_shared_memory(),
							 errmsg("could not duplicate handle for \"%s\": %m",
									fc_name)));
				}

				/*
				 * 在这里，我们记住了我们在postmaster进程中创建的句柄。这个句柄实际上在除postmaster以外的任何进程中都无法使用，但这无关紧要。我们只是持有它，以便在段被取消固定时，dsm_impl_unpin_segment可以关闭它。
				 */
				*fc_impl_private_pm_handle = hmap;
			}
			break;
#endif
		default:
			break;
	}
}

/*
 * 当段不再需要被保留，以便在所有后端都已从中分离时清理它，必须执行的特定于实现的操作。
 *
 * 除非在Windows上，我们实际上不需要做任何事情。对于Windows，我们关闭在postmaster的进程空间中由dsm_impl_pin_segment创建的额外句柄。
 */
void dsm_impl_unpin_segment(dsm_handle fc_handle, void **fc_impl_private)
{
	switch (dynamic_shared_memory_type)
	{
#ifdef USE_DSM_WINDOWS
		case DSM_IMPL_WINDOWS:
			if (IsUnderPostmaster)
			{
				if (*fc_impl_private &&
					!DuplicateHandle(PostmasterHandle, *fc_impl_private,
									 NULL, NULL, 0, FALSE,
									 DUPLICATE_CLOSE_SOURCE))
				{
					char		name[64];

					snprintf(name, 64, "%s.%u", SEGMENT_NAME_PREFIX, fc_handle);
					_dosmaperr(GetLastError());
					ereport(ERROR,
							(errcode_for_dynamic_shared_memory(),
							 errmsg("could not duplicate handle for \"%s\": %m",
									name)));
				}

				*fc_impl_private = NULL;
			}
			break;
#endif
		default:
			break;
	}
}

static int fc_errcode_for_dynamic_shared_memory(void)
{
	if (errno == EFBIG || errno == ENOMEM)
		return errcode(ERRCODE_OUT_OF_MEMORY);
	else
		return errcode_for_file_access();
}
